package utils;

import utils.ioclasses.FileStringPrintWriter;
import utils.ioclasses.NullOutputStream;
import utils.ioclasses.StderrPrintWriter;
import utils.ioclasses.StdoutPrintWriter;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

public class IOUtils {

    private static final OutputStream NULL_OUTPUT_STREAM = new NullOutputStream();

    public static final PrintWriter NULL_PRINT_WRITER = new PrintWriter(NULL_OUTPUT_STREAM);
    public static final Scanner NULL_SCANNER = new Scanner("");
    public static PrintWriter initWriter(String filePath) throws IOException {
        switch (filePath) {
            case "_stdin", "_null" -> {
                return NULL_PRINT_WRITER;
            }
            case "_stdout" -> {
                return new StdoutPrintWriter();
            }
            case "_stderr" -> {
                return new StderrPrintWriter();
            }
            default -> {
                return new PrintWriter(new FileWriter(filePath, false));
            }
        }
    }

    public static FileStringPrintWriter initFileStringWriter(String filePath) throws IOException {
        switch (filePath) {
            case "_stdin", "_null" -> {
                return new FileStringPrintWriter(NULL_PRINT_WRITER, new StringWriter());
            }
            case "_stdout" -> {
                return new FileStringPrintWriter(new StdoutPrintWriter(), new StringWriter());
            }
            case "_stderr" -> {
                return new FileStringPrintWriter(new StderrPrintWriter(), new StringWriter());
            }
            default -> {
                return new FileStringPrintWriter(new PrintWriter(new FileWriter(filePath)), new StringWriter());
            }
        }
    }

    public static Scanner intiScanner(String filePath) throws IOException {
        switch (filePath) {
            case "_stdout", "_stderr", "_null" -> {
                return NULL_SCANNER;
            }
            case "_stdin" -> {
                return new Scanner(System.in);
            }
            default -> {
                return new Scanner(new File(filePath), StandardCharsets.UTF_8);
            }
        }
    }


    public static boolean compareStringFile(String string, String filePath, String name1) {
        try (Scanner scanner1 = new Scanner(string);
             Scanner scanner2 = new Scanner(new File(filePath))) {
            return compareWorker(scanner1, scanner2, name1, filePath);
        }
        catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    public static boolean compareFiles(String filePath1, String filePath2) {
        try (Scanner scanner1 = new Scanner(new File(filePath1));
             Scanner scanner2 = new Scanner(new File(filePath2))) {
            return compareWorker(scanner1, scanner2, filePath1, filePath2);
        }
        catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    private static boolean compareWorker(Scanner scanner1, Scanner scanner2, String name1, String name2) {
        System.out.printf("Comparing %s and %s: ", name1, name2);
        String line1 = null, line2 = null;
        boolean has1, has2;
        for (int lineCount = 1; ; lineCount++) {
            has1 = scanner1.hasNextLine();
            has2 = scanner2.hasNextLine();
            if (has1) {
                line1 = scanner1.nextLine();
            }
            if (has2) {
                line2 = scanner2.nextLine();
            }
            if (!has1 && has2) {
                System.out.printf("%s is shorter than %s.%n", name1, name2);
                System.out.printf("%s:%d\t<null>%n", name1, lineCount);
                System.out.printf("%s:%d\t%s%n", name2, lineCount, line2);
                return false;
            }
            else if (has1 && !has2) {
                System.out.printf("%s is longer than %s.%n", name1, name2);
                System.out.printf("%s:%d\t%s%n", name1, lineCount, line1);
                System.out.printf("%s:%d\t<null>%n", name2, lineCount);
                return false;
            }
            else if (!has1) {
                System.out.printf("OK.%n");
                return true;
            }
            if (!line1.equals(line2)) {
                System.out.printf("Not the same.%n");
                System.out.printf("%s:%d\t%s%n", name1, lineCount, line1);
                System.out.printf("%s:%d\t%s%n", name2, lineCount, line2);
                return false;
            }
        }
    }

}
